using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Localization;
using Abp.Runtime.Session;
using Abp.UI;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.Dto;
using Mt.Site.Application.Taxonomys.Dto;
using Mt.Site.Application.Taxonomys.FrontDto;
using Mt.Site.Core.Base.Localization;
using Mt.Site.Core.Navigations;
using Mt.Site.Core.Taxonomys;
using Mt.Site.Core.Taxonomys.Entities;
using Mt.Site.Core.Taxonomys.Permissions;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Mt.Site.Application.Taxonomys
{
    public class TaxonomyAppService: MtSiteAppServiceBase, ITaxonomyAppService
    {
        private MyLocalizationHelper _navLocalizeHelper
            = new MyLocalizationHelper(MtSiteConsts.LocalizationSourceName_Navigation);

        private readonly ITaxonomysManager _taxonomysManager;
        private readonly IRepository<Taxonomy, Guid> _taxonomyRepository;
        private readonly IRepository<TaxonomysItem, Guid> _taxonomysItemRepository;
        private readonly IRepository<TaxonomysItemBind, Guid> _taxonomysItemBindRepository;
        private readonly IApplicationLanguageTextManager _applicationLanguageTextManager;
        private readonly ITaxonomyFrontAppService _taxonomyFrontAppService;
        private readonly IMenuStore _menuStore;

        public TaxonomyAppService(
            ITaxonomysManager taxonomysManger,
            IRepository<Taxonomy, Guid> taxonomyRepository,
            IRepository<TaxonomysItem, Guid> taxonomysItemRepository,
            IRepository<TaxonomysItemBind, Guid> taxonomysItemBindRepository,
            IApplicationLanguageTextManager applicationLanguageTextManager,
            ITaxonomyFrontAppService taxonomyFrontAppService,
            IMenuStore menuStore)
        {
            _taxonomysManager = taxonomysManger;
            _taxonomyRepository = taxonomyRepository;
            _taxonomysItemRepository = taxonomysItemRepository;
            _taxonomysItemBindRepository = taxonomysItemBindRepository;
            _applicationLanguageTextManager = applicationLanguageTextManager;

            _taxonomyFrontAppService = taxonomyFrontAppService;
            _menuStore = menuStore;
        }

        #region taxonomy
        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Query)]
        public async Task<PagedResultDto<PagedTaxonomyOut>> GetPagedTaxonomyAsync(PagedTaxonomyInput input)
        {
            var query = _taxonomyRepository.GetAll()
                        .WhereIf(!string.IsNullOrWhiteSpace(input.Filter), item => item.Title.Contains(input.Filter));

            query = string.IsNullOrWhiteSpace(input.Sorting) ?
                    query.OrderByDescending(item => item.Norder)
                         .ThenByDescending(item => item.CreationTime)
                     : query.OrderBy(input.Sorting);
            var count = query.Count();
            var list = await query.PageBy(input).ToListAsync();
            var ids = new List<string>();
            list.ForEach(item => {
                ids.Add(item.Id.ToString());
            });

            list.ForEach(m =>
            {
                m.LocalDisplayName = _navLocalizeHelper.GetStringOrNull(m.Id.ToString(), CultureInfo.GetCultureInfo(AbpSession.CultureName()));
            });
            return new PagedResultDto<PagedTaxonomyOut>(count, ObjectMapper.Map<List<PagedTaxonomyOut>>(list));

        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Create)]
        public async Task CreateTaxonomyAsync(CreateTaxonomyInput input)
        {
            var entity = ObjectMapper.Map<Taxonomy>(input);
            var result = await _taxonomysManager.CreateTaxonomysAsync(entity);
            if (!string.IsNullOrWhiteSpace(input.LocalDisplayName))
            {
                await _applicationLanguageTextManager.AddOrUpdateStringAsync(
                    AbpSession.GetTenantId(), 
                    AbpSession.CultureName(), 
                    result.Id.ToString(), input.LocalDisplayName);
            }
        }
        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task UpdateTaxonomyAsync(UpdateTaxonomyInput input)
        {
            var entity = await _taxonomyRepository.GetAsync(input.Id);
            entity.IsActive = input.IsActive;
            entity.IsTree = input.IsTree;
            entity.Norder = input.Norder;
            entity.Title = input.Title;
            await _taxonomysManager.UpdateTaxonomysAsync(entity);

            await _applicationLanguageTextManager.AddOrUpdateStringAsync(
                    AbpSession.GetTenantId(),
                    AbpSession.CultureName(),
                    entity.Id.ToString(),
                    input.LocalDisplayName);
        }
        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Delete)]
        public async Task DeleteTaxonomyAsync(EntityIdListDto  input)
        {
            foreach (var item in input.List)
            {
                await _taxonomysManager.DeleteTaxonomyAsync(item,AbpSession.GetTenantId(), AbpSession.CultureName());

                await _applicationLanguageTextManager.AddOrUpdateStringAsync(
                            AbpSession.GetTenantId(),
                            AbpSession.CultureName(),
                            item.ToString(),
                            null);
            }
        }
        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task SetTaxonomyStatus(SetStatusInput input)
        {
            var list = await _taxonomyRepository.GetAllListAsync(item => input.IdList.Contains(item.Id));

            foreach (var item in list)
            {
                item.IsActive = input.IsActive;
                await _taxonomysManager.UpdateTaxonomysAsync(item);
            }
        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Query)]
        public MaxOrderInfoOut GetMaxTaxonomyOrderInfo()
        {
            var query = from q in _taxonomyRepository.GetAll()
                        orderby q.Norder descending
                        select q;
            var m = query.FirstOrDefault();

            int order;
            if (m != null)
            {
                order = m.Norder + 1;
            }
            else
            {
                order = 1;
            }

            return new MaxOrderInfoOut
            {
                Norder = order
            };
        }
        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task SetNewTaxonomyOrderInfoAsync(NewSortNumberInput input)
        {
            var entity = await _taxonomyRepository.FirstOrDefaultAsync(input.Id);
            if (entity != null)
            {
                entity.Norder = input.NewNumber;
                await _taxonomysManager.UpdateTaxonomysAsync(entity);
            }
        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task SwapTaxonomyObjectSortAsync(SwapSortNumberInput input)
        {
            var idList = input.IdOrderList.Select(c => c.Id).ToList();
            var orderList = input.IdOrderList.Select(c => c.Order).ToList();

            var list = await _taxonomyRepository.GetAllListAsync(c => idList.Contains(c.Id));

            foreach (var item in list)
            {
                var index = idList.FindIndex(c => c == item.Id);
                if (index != -1)
                {
                    item.Norder = orderList[index];
                    await _taxonomysManager.UpdateTaxonomysAsync(item);
                }
            }
        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Query)]
        public async Task<List<TaxonomyListOut>> GetTaxonomyListAsync()
        {
            var list = await _taxonomyRepository.GetAllListAsync(item => item.IsActive);
            var ids = new List<string>();
            list.ForEach(item => {
                ids.Add(item.Id.ToString());
            });
            list.ForEach(item =>
            {
                item.LocalDisplayName = _navLocalizeHelper.GetStringOrNull(item.Id.ToString(), CultureInfo.GetCultureInfo(AbpSession.CultureName()));
            });
            return ObjectMapper.Map<List<TaxonomyListOut>>(list);
        }
        #endregion


        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Query)]
        public async Task<List<TaxonomyItemDataOut>> GetTaxonomyItemTree(TaxonomyItemListInput input)
        {
            var list = await _taxonomysItemRepository.GetAllListAsync(item => input.TypeId == item.TypeId);

            var ids = new List<string>();
            list.ForEach(item => {
                ids.Add(item.Id.ToString());
            });

            var itemBinds = await _taxonomysItemBindRepository.GetAllListAsync(item => ids.Contains(item.ItemId.ToString()));

            var result = ObjectMapper.Map<List<TaxonomyItemDataOut>>(list);
            result.ForEach(n =>
            {
                n.LocalDisplayName = _navLocalizeHelper.GetStringOrNull(n.Id.ToString(), CultureInfo.GetCultureInfo(AbpSession.CultureName()));

                itemBinds.ForEach(item =>
                {
                    if (n.Id == item.ItemId)
                    {
                        n.IsBindItem = true;
                    }
                });
            });

            return result;
        }


        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Create)]
        public async Task<TaxonomyItemDataOut> CreateTaxonomyItemAsync(CreateTaxonomyItemInput input)
        {
            var entity = ObjectMapper.Map<TaxonomysItem>(input);
            entity.LocalDisplayName = input.LocalDisplayName;
            entity.IsBindItem = input.IsBindItem;

            await CheckTaxonmomyItemValue(input.TypeId, input.Value, true, null);

            var result = await _taxonomysManager.CreateTaxonomysItemAsync(entity);

            if (input.IsBindItem)
            {
                await _taxonomysManager.CreateTaxonomysItemBindAsync(result.Id);
            }

            if (!string.IsNullOrWhiteSpace(input.LocalDisplayName))
            {
                await _applicationLanguageTextManager.AddOrUpdateStringAsync(
                    AbpSession.GetTenantId(), AbpSession.CultureName(), result.Id.ToString(), input.LocalDisplayName);
            }
            return ObjectMapper.Map<TaxonomyItemDataOut>(result);

        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task<TaxonomyItemDataOut> UpdateTaxonomyItemAsync(UpdateTaxonomyItemInput input)
        {
            var entity =await _taxonomysItemRepository.GetAsync(input.Id);
            await CheckTaxonmomyItemValue(entity.TypeId, input.Value, false, entity.Id);
            entity.IsActive = input.IsActive;
            entity.Norder = input.Norder;
            entity.DisplayName = input.DisplayName;
            entity.Value = input.Value;
            entity.Code = input.Code;
            entity.IsBindItem = input.IsBindItem;
            entity.LocalDisplayName = input.LocalDisplayName;
            entity.Pic1 = input.Pic1;
            entity.Pic2 = input.Pic2;
            entity.LinkUrl = input.LinkUrl;
            await _taxonomysManager.UpdateTaxonomysItemAsync(entity);

            if (input.IsBindItem)
            {
                await _taxonomysManager.CreateTaxonomysItemBindAsync(entity.Id);
            }
            else
            {
                await _taxonomysManager.DeleteTaxonomysItemBindAsync(entity.Id);
            }

            await _applicationLanguageTextManager.AddOrUpdateStringAsync(AbpSession.GetTenantId(),
                    AbpSession.CultureName(), entity.Id.ToString(), entity.LocalDisplayName);

            return ObjectMapper.Map<TaxonomyItemDataOut>(entity);

        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Delete)]
        public async Task DeleteTaxonomyItemAsync(EntityIdListDto  input)
        {
            var list = await _taxonomysItemRepository.GetAllListAsync(item => input.List.Contains(item.Id));

            foreach (var item in list)
            {
                await _taxonomysManager.DeleteTaxonomysItemAsync(item.Id, AbpSession.GetTenantId(), AbpSession.CultureName(), item.TypeId);
            }
        }
        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task<TaxonomyItemDataOut> SetTaxonomyItemStatus(SetTaxonomyItemStatusInput input)
        {
            var entity = await _taxonomysItemRepository.GetAsync(input.Id);
            entity.IsActive = input.IsActive;
            var result =  await _taxonomysManager.UpdateTaxonomysItemAsync(entity);
            await GetLocalValueAndItemBind(result);
            return ObjectMapper.Map<TaxonomyItemDataOut>(result);
        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task MoveTaxonomyItemAsync(MoveItemInput input)
        {
            await _taxonomysManager.MoveAsync(input.Id, input.ParentId);
        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task<ListResultDto<TaxonomyItemDataOut>> UpTaxonomyItemSort(EntityDto<Guid> input)
        {
            var list = await _taxonomysManager.ChangeTaxonomyItemSort(input.Id, true);
            foreach (var item in list)
            {
                await GetLocalValueAndItemBind(item);
            }
            return new ListResultDto<TaxonomyItemDataOut> {
                Items = ObjectMapper.Map<IReadOnlyList<TaxonomyItemDataOut>>(list)
            };
        }

        [AbpAuthorize(TaxonomyPermissions.Pages_Manage_Taxonomy_Update)]
        public async Task<ListResultDto<TaxonomyItemDataOut>> DownTaxonomyItemSort(EntityDto<Guid> input)
        {
            var list = await _taxonomysManager.ChangeTaxonomyItemSort(input.Id, false);
            foreach (var item in list)
            {
                await GetLocalValueAndItemBind(item);
            }
            return new ListResultDto<TaxonomyItemDataOut>
            {
                Items = ObjectMapper.Map<IReadOnlyList<TaxonomyItemDataOut>>(list)
            };
        }

        public async Task<List<TaxonomyFrontItem>> GetTaxonomyCollectionsAsync(EntityDto<string> input)
        {
            if(!input.Id.IsNullOrEmpty())
            {
                Guid id;

                if (!Guid.TryParse(input.Id, out id))
                {
                    id = _menuStore.GetGuidValueByKeyFromConfig(input.Id);
                }

                if(id != Guid.Empty)
                {
                    using (CurrentUnitOfWork.SetCultureName(AbpSession.CultureName()))
                    {
                        return await _taxonomyFrontAppService.GetTaxonomyCollectionsAsync(new EntityDto<Guid>(id));
                    }
                }
            }

            return new List<TaxonomyFrontItem>();
        }

        public async Task<List<TaxonomyItemLevel1Dto>> GetTaxonomyItemLevel1(EntityDto<string> input)
        {
            if (!input.Id.IsNullOrEmpty())
            {
                Guid id;

                if (!Guid.TryParse(input.Id, out id))
                {
                    id = _menuStore.GetGuidValueByKeyFromConfig(input.Id);
                }

                if (id != Guid.Empty)
                {
                    using (CurrentUnitOfWork.SetCultureName(AbpSession.CultureName()))
                    {
                        return await _taxonomyFrontAppService.GetTaxonomyItemLevel1(new EntityDto<Guid>(id));
                    }
                }
            }

            return new List<TaxonomyItemLevel1Dto>();
        }

        public async Task<List<TaxomonyItemNext>> GetTaxonomyLevelNext(EntityDto<Guid> input)
        {
            using (CurrentUnitOfWork.SetCultureName(AbpSession.CultureName()))
            {
                return await _taxonomyFrontAppService.GetTaxonomyLevelNext(input);
            }
        }

        private async Task GetLocalValueAndItemBind(TaxonomysItem entity)
        {
            entity.LocalDisplayName = _navLocalizeHelper.GetStringOrNull(entity.Id.ToString(), CultureInfo.GetCultureInfo(AbpSession.CultureName()));

            var itemBind = await _taxonomysItemBindRepository.FirstOrDefaultAsync(
                            item => item.ItemId == entity.Id);
            if (itemBind != null)
            {
                entity.IsBindItem = true;
            }
            else
            {
                entity.IsBindItem = false;
            }
        }

        private async Task CheckTaxonmomyItemValue(Guid typeId, string value, bool isCreate, Guid? itemId)
        {
            var query = _taxonomysItemRepository.GetAll()
                            .WhereIf(isCreate, item => item.TypeId == typeId && item.Value == value)
                            .WhereIf(!isCreate, item => item.TypeId == typeId && item.Value == value && item.Id != itemId);

            var entity = await query.FirstOrDefaultAsync();

            if (entity != null)
            {
                throw new UserFriendlyException(L("TaxonomyItemValueRepeatError"));
            }
        }

    }
}
